package thread.wang_bao_ling.c22.executors.wheel;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 简单的netty时间轮实现（不管多线程问题和性能问题）
 * @Auther: luobiao
 * @Date: 2021/3/20 10:19
 * @Description:
 */
public class MyHashedWheelTimer {
    private TimeUnit timeUnit;
    private List<MyHashedWheelTimeout>[] array; //这里再netty中其实是HashedWheelBucket数组的，HashedWheelBucket中保存着MyHashedWheelTimeout链表的head和tail节点引用，这里简单点就直接用list代替了
    private int currentIndex = 0;
    private int tickDuration;
    private ExecutorService poolExecutor = Executors.newFixedThreadPool(3);

    public MyHashedWheelTimer(TimeUnit timeUnit, int tickDuration, int ticksPerWheel) {
        this.timeUnit = timeUnit;
        this.array = new List[ticksPerWheel];
        this.tickDuration=tickDuration;
        start(); //这里直接在构造中启动，netty中启动工作线程是在add方法中触发启动的
    }

    /**
     * 启动时间轮线程
     */
    public void start(){
        new Thread(()->{
            while (true) {
                //System.out.println("指针跳动，当前下标=" + currentIndex);
                poolExecutor.execute(()->{
                    execTask(currentIndex);
                });
                try {
                    timeUnit.sleep(tickDuration);
                    if (currentIndex == array.length - 1) {
                        currentIndex = 0;
                    }else{
                        currentIndex++;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    /**
     * 执行时间格中的任务
     * @param currentIndex
     */
    public void execTask(int currentIndex){
        List<MyHashedWheelTimeout> list = array[currentIndex];
        if (list != null) {
            Iterator<MyHashedWheelTimeout> iterator=list.iterator();
            while (iterator.hasNext()) {
                MyHashedWheelTimeout task = iterator.next();
                if (task.getRound() == 0) {
                    task.run();
                    iterator.remove();
                }else{
                    task.setRound(task.getRound() - 1);
                }
            }
        }
    }

    /**
     * 添加定时任务
     * @param run
     * @param execTime
     * @param timeUnit
     * @return
     */
    public int add(Runnable run,long execTime,TimeUnit timeUnit){
        execTime=this.timeUnit.convert(execTime, timeUnit);
        long currentTime = this.timeUnit.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        long diffTime = (execTime - currentTime)/tickDuration;
        int round = (int)diffTime / array.length;
        int index = (int)((diffTime % array.length) + currentIndex);
        if (index >= array.length) {
            round++;
            index -= array.length;
        }
        MyHashedWheelTimeout task = new MyHashedWheelTimeout( run,round);
        if (array[index] == null) {
            List list = new ArrayList();
            list.add(task);
            array[index] = list;
        }else{
            array[index].add(task);
        }
        return array[index].size();
    }

}
